package halton;

import java.util.NoSuchElementException;

import umontreal.iro.lecuyer.rng.RandomStream;

public class OurHalton implements RandomStream {
	
	private int	       number;
	
	private int	       dimension;
	
	private double[][]	haltonSeq;
	
	private int	       pointIndex;
	
	private int	       coordinateIndex;
	
	public OurHalton(int n, int dim) {
		this.number = n;
		this.dimension = dim;
		pointIndex = 0;
		coordinateIndex = -1;
		haltonSeq = generate(n, dim);
	}
	
	public int getNumber() {
		return number;
	}
	
	public void setNumber(int number) {
		this.number = number;
	}
	
	public int getDimension() {
		return dimension;
	}
	
	public void setDimension(int dimension) {
		this.dimension = dimension;
	}
	
	public double[][] getHaltonSeq() {
		return haltonSeq;
	}
	
	public double[][] regenerate() {
		return generate(number, dimension);
	}
	
	public static double[][] generate(int n, int dim) {
		int[] primeNumbers = getPrimeNumbers(dim);
		double[][] result = new double[n][dim];
		for (int i = 0; i < dim; i++) {
			double[] tmp = seq(n, primeNumbers[i]);
			for (int j = 0; j < n; j++) {
				result[j][i] = tmp[j];
			}
		}
		return result;
	}
	
	public static double[] badSeq(int n, int base) {
		int inserIndex = 0;
		double b = (double) base;
		double p0;
		double phase = 0;
		double[] result = new double[n];
		while (inserIndex < n) {
			p0 = 0;
			double i = 1;
			while (i <= Math.pow(b, phase) && inserIndex < n) {
				// for(double i = 1 ; i <= Math.pow( b, phase ) ; i++){
				double borneSup = i / Math.pow(b, phase);
				double j = 1;
				while (j < b && inserIndex < n) {
					// for (double j = 1 ; j < b ; j++){
					result[inserIndex] = p0 + (borneSup - p0) * j / b;
					inserIndex++;
					j++;
				}
				p0 = borneSup;
				i++;
			}
			phase++;
		}
		
		return result;
	}
	
	public static double[] seq(int n, int base) {
		double[] result = new double[n];
		for (int i = 0; i < n; i++) {
			result[i] = getNum(i, base);
		}
		return result;
	}
	
	public static double getNum(int index, int base) {
		double result = 0;
		double f = 1 / (double) base;
		double i = index;
		while (i > 0) {
			double mod = i % base;
			result = result + f * mod;
			i = (double) (i - mod) / (double) base;
			f = f / (double) base;
		}
		return result;
	}
	
	// public static double getNum(int index, int base){
	// double result = 0;
	// double f = 1 / base;
	// double i = index;
	// while(i > 0){
	// result = result + f * (i % base);
	// i = Math.floor(i /(double) base);
	// f = f / (double)base;
	// }
	// return result;
	// }
	
	public static int[] getPrimeNumbers(int n) {
		int nb = 1;
		int[] primeNumbers = new int[n];
		primeNumbers[0] = 2;
		int i = 3;
		while (nb < n) {
			int j = 0;
			boolean estDivisible = false;
			while (j < nb && !estDivisible && primeNumbers[j] <= Math.sqrt(i)) {
				if (i % primeNumbers[j] == 0) {
					estDivisible = true;
				}
				j++;
			}
			if (!estDivisible) {
				primeNumbers[nb] = i;
				nb++;
			}
			i += 2;
		}
		return primeNumbers;
	}
	
	public static void main(String[] args) {
		int n = 50;
		int d = 5;
		double t1 = System.currentTimeMillis();
		double[][] result = generate(n, d);
		Halton sequence = new Halton(n, d);
		double t2 = System.currentTimeMillis();
		System.out.println("premiere methode : " + (t2 - t1));
		
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < d; j++) {
				System.out.print(result[i][j] + "\t");
			}
			System.out.println("");
		}
		System.out.println("seconde methode : " + (t2 - t1));
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < d; j++) {
				System.out.print(sequence.getCoordinate(i, j) + "\t");
			}
			System.out.println("");
		}
		
		// System.out.println("\n\n\nHalton seq \n");
		// PointSet h = new SobolSequence(n, d);
		// RandomStream noise = new MRG32k3a();
		// for (int i = 0; i <= n; i++) {
		// System.out.println(" i = "+i);
		// for (int j = 0; j <= d; j++) {
		// try{
		//
		// System.out.print(noise.nextDouble() + "\t");
		// }catch(NoSuchElementException e){
		// System.out.print("err\t");
		// }
		// }
		// System.out.println("");
		// }
		
	}
	
	@Override
	public void nextArrayOfDouble(double[] arg0, int arg1, int arg2) {
		
	}
	
	@Override
	public void nextArrayOfInt(int arg0, int arg1, int[] arg2, int arg3, int arg4) {
		
	}
	
	@Override
	public double nextDouble() throws NoSuchElementException {
		coordinateIndex++;
		double result = 0;
		try {
			result = haltonSeq[pointIndex][coordinateIndex];
		} catch (IndexOutOfBoundsException e) {
			resetNextSubstream();
			throw new NoSuchElementException("Not enougth coordinates available");
		} finally {
			
		}
		return result;
	}
	
	@Override
	public int nextInt(int arg0, int arg1) {
		return 0;
	}
	
	@Override
	public void resetNextSubstream() throws NoSuchElementException {
		pointIndex++;
		coordinateIndex = -1;
		if (pointIndex >= number) {
			resetStartStream();
			throw new NoSuchElementException("Not enougth coordinates available");
		}
		
	}
	
	@Override
	public void resetStartStream() {
		pointIndex = 0;
		coordinateIndex = -1;
		
	}
	
	@Override
	public void resetStartSubstream() {
		coordinateIndex = -1;
		
	}
	
}